package middleware

import (
	"errors"
	"github.com/gorilla/websocket"
	"go-gin-api/app/model"
	"sync"
)

type Connection struct {
	wsConn    *websocket.Conn //webSocket 句柄
	inChan    chan []byte     // 接收客户端消息协程
	outChan   chan []byte     // 发消息给客户端协程
	closeChan chan int        // 协程间通讯  通知其他协程关闭
	isClosed  bool            // 状态锁 仅允许执行一次关闭 channel
	mutex     sync.Mutex      //加锁
}

//初始化webSocket服务
func InitConnection(wsConn *websocket.Conn, user model.UserInfo) (conn *Connection, err error) {
	conn = &Connection{
		wsConn:    wsConn,
		inChan:    make(chan []byte, 1000),
		outChan:   make(chan []byte, 1000),
		closeChan: make(chan int, 1),
		isClosed:  false,
	}

	//启动收消息协程
	go conn.readLoop(user)

	//启动发消息协程
	go conn.writeLoop(user)

	return
}

//读取消息API
func (conn *Connection) ReadMessage() (data []byte, err error) {
	select { // 防止inChan 无数据 客户端等待卡死
	case data = <-conn.inChan:
	case <-conn.closeChan:
		err = errors.New("connection is closed")
	}
	return
}

//写入消息API
func (conn *Connection) WriteMessage(data []byte) (err error) {
	select {
	case conn.outChan <- data:
	case <-conn.closeChan:
		err = errors.New("connection is closed")
	}
	return
}

//关闭链接
func (conn *Connection) Close() {
	// 线程安全，可重入的Close
	conn.wsConn.Close()

	//关闭chan 这行代码只能执行一次
	conn.mutex.Lock() //加锁
	if !conn.isClosed {
		close(conn.closeChan)
		conn.isClosed = true
	}
	conn.mutex.Unlock() //释放锁
}

//内部实现

// 循环读取客户端消息
func (conn *Connection) readLoop(user model.UserInfo) {
	var (
		data []byte
		err  error
	)
	for {
		// 得到客户端消息
		if _, data, err = conn.wsConn.ReadMessage(); err != nil {
			goto ERR
		}
		//messageInfo,err1:=impl.Parse(data)
		//	if err1!=nil{
		//	fmt.Println("消息接收错误:",err1)
		//	}
		//if messageInfo.To==user.Id||messageInfo.MType==2{
		//将数据写入
		select {
		case conn.inChan <- data:
		case <-conn.closeChan:
			//closeChan 关闭
			goto ERR
		}
		//	}

	}
ERR:
	//TODO 如果出错 就关闭链接
	conn.Close()
}

//循环发送消息给客户端
func (conn *Connection) writeLoop(user model.UserInfo) {
	var (
		data []byte
		err  error
	)
	for {

		// 取数据
		select {
		case data = <-conn.outChan:
			//fmt.Println("writeLoop:" + string(data))
		case <-conn.closeChan:
			goto ERR
		}
		// 发送给客服端
		if err = conn.wsConn.WriteMessage(websocket.TextMessage, data); err != nil {
			goto ERR
		}
	}

ERR:
	//TODO 如果出错 就关闭链接
	conn.Close()
}
